/* Copyright (C) 2012  Chris Mueller <ruunsmail@googlemail.com>
 *
 * This file is part of the Liquid Programming Language
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "structures.h"
#include <stdlib.h>
#include <assert.h>

#define INITIAL_CAPACITY 4

struct LqStack {
    pointer* begin;
    size_t length;
    size_t capacity;
};


struct LqStack*
lq_stack_new(void)
{
	struct LqStack* stack = malloc(sizeof(struct LqStack));

	stack->begin = calloc(INITIAL_CAPACITY, sizeof(pointer));
	stack->capacity = INITIAL_CAPACITY;
	stack->length = 0;

	return stack;
}


void
lq_stack_free(struct LqStack* s)
{
	assert(s != 0);

	free(s->begin);
	free(s);
}


size_t
lq_stack_size(struct LqStack* s)
{
	assert(s != 0);

	return s->length;
}


void
lq_stack_push(struct LqStack* s, const pointer item)
{
	assert(s != 0);

	if(s->length >= s->capacity) {
		s->capacity = s->capacity * 2;
		s->begin = realloc(s->begin, s->capacity * sizeof(pointer));
	}

	*(s->begin + s->length) = item;
        
        ++(s->length);
}


pointer
lq_stack_pop(struct LqStack* s)
{
	assert(s != 0);

	if(s->length > 0) {
		s->length--;
		return *(s->begin + s->length);
	}

	return 0;
}


pointer
lq_stack_top(struct LqStack* s)
{
	assert(s != 0);

	return (s->length > 0) ? *(s->begin + s->length - 1) : 0;
}


pointer 
lq_stack_below(struct LqStack* s, size_t index)
{
	assert(s != 0);

	return (index < s->length) ? *(s->begin + s->length - index - 1) : 0;
}





struct Node {
	struct Node* next;
	pointer data;
};


struct LqQueue {
	struct Node* front;
	struct Node* rear;
};

struct LqQueue*  	
lq_queue_new(void)
{
	struct LqQueue* list = malloc(sizeof(struct LqQueue));
	list->front = 0;
	list->rear = 0;
	return list;
}


void            	
lq_queue_free(struct LqQueue* queue, lq_free_funptr handler)
{
	assert(queue != 0);

	struct Node* node = queue->front;
	struct Node* tmp = 0;

	while(node != 0) {
		tmp = node;
		node = node->next;

		if(handler != 0) {
			handler(tmp->data);
		}

		free(tmp);
	}

	free(queue);
}


int             	
lq_queue_size(struct LqQueue* queue)
{
	assert(queue != 0);

	size_t length = 0;
	struct Node* node = queue->front;

	while(node != 0) {
		node = node->next;
		++length;
	}

	return length;
}


pointer         	
lq_queue_front(struct LqQueue* queue)
{
	assert(queue != 0);

	if(queue->front == 0)
		return 0;
	else
		return queue->front->data;
}


pointer
lq_queue_rear(struct LqQueue* queue)
{
	assert(queue != 0);

	if(queue->rear == 0)
		return 0;
	else
		return queue->rear->data;
}


pointer
lq_queue_nth(struct LqQueue* queue, size_t index)
{
	assert(queue != 0);

	struct Node* node = queue->front;

	while(node != 0 && index-- > 0)
		node = node->next;

	return (node != 0) ? node->data : 0;
}


pointer         	
lq_queue_dequeue(struct LqQueue* queue)
{
	assert(queue != 0);

	struct Node* front = queue->front;
	pointer data = 0;

	if(front == 0) 
		return data;

	data = front->data;

	if(queue->front == queue->rear) 
		queue->front = queue->rear = 0;
	else
		queue->front = front->next;

	free(front);

	return data;
}


void            	
lq_queue_enqueue(struct LqQueue* queue, pointer data)
{
	assert(queue != 0);

	struct Node* node = malloc(sizeof(struct Node));

	node->next = 0;
	node->data = data;

	if(queue->front == 0) {
		queue->front = node;
		queue->rear = node;
	 } else {
		queue->rear->next = node;
		queue->rear = node;
	}
}




